/*
 *
 *  * Unidata Platform
 *  * Copyright (c) 2013-2020, UNIDATA LLC, All rights reserved.
 *  *
 *  * Commercial License
 *  * This version of Unidata Platform is licensed commercially and is the appropriate option for the vast majority of use cases.
 *  *
 *  * Please see the Unidata Licensing page at: https://unidata-platform.com/license/
 *  * For clarification or additional options, please contact: info@unidata-platform.com
 *  * -------
 *  * Disclaimer:
 *  * -------
 *  * THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND
 *  * REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE
 *  * IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY,
 *  * FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND
 *  * THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING.
 *
 */
package org.unidata.mdm.rest.v1.dq.core.converter;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.unidata.mdm.core.type.data.ArrayAttribute.ArrayDataType;
import org.unidata.mdm.core.type.data.Attribute;
import org.unidata.mdm.core.type.data.SimpleAttribute.SimpleDataType;
import org.unidata.mdm.core.type.data.impl.AbstractArrayAttribute;
import org.unidata.mdm.core.type.data.impl.AbstractSimpleAttribute;
import org.unidata.mdm.core.type.model.AttributeElement.AttributeValueType;
import org.unidata.mdm.core.type.model.instance.AbstractCustomPropertiesImpl;
import org.unidata.mdm.core.util.AttributeUtils;
import org.unidata.mdm.dq.core.context.CleanseFunctionContext;
import org.unidata.mdm.dq.core.type.cleanse.CleanseFunctionInputParam;
import org.unidata.mdm.rest.core.converter.CustomPropertiesConverter;
import org.unidata.mdm.rest.system.converter.Converter;
import org.unidata.mdm.rest.v1.dq.core.ro.ExecuteFunctionRequestRO;
import org.unidata.mdm.rest.v1.dq.core.ro.constant.AbstractConstantRO;
import org.unidata.mdm.rest.v1.dq.core.ro.constant.ArrayConstantRO;
import org.unidata.mdm.rest.v1.dq.core.ro.constant.ArrayValueRO;
import org.unidata.mdm.rest.v1.dq.core.ro.constant.SingleConstantRO;
import org.unidata.mdm.rest.v1.dq.core.ro.constant.SingleValueRO;
import org.unidata.mdm.rest.v1.dq.core.ro.execute.FunctionExecutionParamRO;
import org.unidata.mdm.system.util.TextUtils;

/**
 * The Class CleanseFunctionDataConverter.
 */
public class ExecuteFunctionRequestConverter
    extends Converter<ExecuteFunctionRequestRO, CleanseFunctionContext> {
    /**
     * Instantiates a new cleanse function data converter.
     */
    public ExecuteFunctionRequestConverter() {
        super(ExecuteFunctionRequestConverter::from, null);
    }
    /**
     * Convert rest simple to meta simple attr.
     *
     * @param source
     *            the source
     * @param functionName the name of the function to execute
     * @return context
     */
    private static final CleanseFunctionContext from(ExecuteFunctionRequestRO request) {

        Collection<CleanseFunctionInputParam> params = CollectionUtils.isEmpty(request.getParams())
                ? Collections.emptyList()
                : request.getParams().stream()
                    .map(ip -> CleanseFunctionInputParam.of(ip.getPortName(), from(ip)))
                    .collect(Collectors.toList());

        AbstractCustomPropertiesImpl acpi
            = new AbstractCustomPropertiesImpl(request.getCustomProperties().stream()
                .map(CustomPropertiesConverter::from)
                .collect(Collectors.toList())) {};

        return CleanseFunctionContext.builder()
                .functionName(request.getFunctionName())
                .input(params)
                .currentLocale(TextUtils.getCurrentLocale())
                .customProperties(acpi.getCustomProperties())
                .build();
    }

    private static Attribute from(FunctionExecutionParamRO param) {

        if (Objects.isNull(param)
         || Objects.isNull(param.getConstant())
         || StringUtils.isBlank(param.getPortName())) {
            return null;
        }

        AbstractConstantRO value = param.getConstant();
        switch (value.getType()) {
        case ARRAY:
            ArrayValueRO aep = ((ArrayConstantRO) value).getArray();
            ArrayDataType arrayType = ArrayDataType.valueOf(aep.getType());
            return AbstractArrayAttribute.of(
                    arrayType,
                    param.getPortName(),
                    ArrayUtils.isEmpty(aep.getValues())
                        ? ArrayUtils.EMPTY_OBJECT_ARRAY
                        : Arrays.stream(aep.getValues())
                                .map(v -> ensureValue(arrayType.toModelType(), v))
                                .toArray());
        case SINGLE:
            SingleValueRO sep = ((SingleConstantRO) value).getSingle();
            SimpleDataType simpleType = SimpleDataType.valueOf(sep.getType());
            return AbstractSimpleAttribute.of(
                    simpleType,
                    param.getPortName(),
                    ensureValue(simpleType.toModelType(), sep.getValue()));
        default:
            break;
        }

        return null;
    }

    private static Object ensureValue(AttributeValueType t, Object in) {

        switch (t) {
        case BOOLEAN:
            return AttributeUtils.toBoolean(in);
        case DATE:
            return AttributeUtils.toDate(in);
        case INTEGER:
            return AttributeUtils.toLong(in);
        case MEASURED:
        case NUMBER:
            return AttributeUtils.toDouble(in);
        case STRING:
            return AttributeUtils.toString(in);
        case TIME:
            return AttributeUtils.toTime(in);
        case TIMESTAMP:
            return AttributeUtils.toTimestamp(in);
        default:
            break;
        }

        return null;
    }
}
